﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;

namespace LINQ
{
    public static class ExtensionMethods
    {
        public static T As<T>(this object source)
        {
            return (T)source;
        }

        public static T NullCheck<T>(this T source, T defaultValue) where T : class
        {
            if (source == null || (source is string && source.As<string>() == ""))
                return defaultValue;

            return source;
        }

        public static IEnumerable<T> NullCheck<T>(this IEnumerable<T> source)
        {
            return source ?? Enumerable.Empty<T>();
        }

        public static T[] Each<T>(this IEnumerable<T> source, Action<T> action)
        {
            return source
                .NullCheck()
                .Select(item =>
                {
                    action(item);
                    return item;
                })
                .ToArray();
        }

        public static T[] Each<T>(this IEnumerable<T> source, Action<T, int> action)
        {
            return source
                .NullCheck()
                .Select((item, index) =>
                {
                    action(item, index);
                    return item;
                })
                .ToArray();
        }

        public static string F(this string source, object arg0)
        {
            return string.Format(source, arg0);
        }

        public static string F(this string source, object arg0, object arg1)
        {
            return string.Format(source, arg0, arg1);
        }

        public static string F(this string source, object arg0, object arg1, object arg2)
        {
            return string.Format(source, arg0, arg1, arg2);
        }

        public static string F(this string source, params object[] args)
        {
            return string.Format(source, args);
        }

        public static int ToInt(this string source)
        {
            return string.IsNullOrEmpty(source) ? 0 : Convert.ToInt32(source);
        }

        public static uint ToUInt(this string source)
        {
            return string.IsNullOrEmpty(source) ? 0U : Convert.ToUInt32(source);
        }

        public static long ToLong(this string source)
        {
            return string.IsNullOrEmpty(source) ? 0L : Convert.ToInt64(source);
        }

        public static ulong ToULong(this string source)
        {
            return string.IsNullOrEmpty(source) ? 0UL : Convert.ToUInt64(source);
        }

        public static decimal ToDecimal(this string source)
        {
            return string.IsNullOrEmpty(source) ? 0M : Convert.ToDecimal(source);
        }

        public static double ToDouble(this string source)
        {
            return string.IsNullOrEmpty(source) ? 0D : Convert.ToDouble(source);
        }

        public static DateTime ToDate(this string source)
        {
            return string.IsNullOrEmpty(source) ? DateTime.MinValue : DateTime.Parse(source);
        }

        public static TimeSpan Seconds(this int seconds)
        {
            return new TimeSpan(0, 0, seconds);
        }

        public static TimeSpan Minutes(this int minutes)
        {
            return new TimeSpan(0, minutes, 0);
        }

        public static TimeSpan Hours(this int hours)
        {
            return new TimeSpan(hours, 0, 0);
        }

        public static byte[] Serialize(this object source)
        {
            using (var memoryStream = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(memoryStream, source);
                return memoryStream.ToArray();
            }
        }

        public static T Deserialize<T>(this byte[] source)
        {
            using (var memoryStream = new MemoryStream(source))
            {
                var formatter = new BinaryFormatter();
                return (T)formatter.Deserialize(memoryStream);
            }
        }

        public static void RunAsync(this Action action)
        {
            action.BeginInvoke(action.EndInvoke, null);
        }

        public static void RunAsync<T>(this Action<T> action, T arg)
        {
            action.BeginInvoke(arg, action.EndInvoke, null);
        }

        public static void RunAsync<T1, T2>(this Action<T1, T2> action, T1 arg1, T2 arg2)
        {
            action.BeginInvoke(arg1, arg2, action.EndInvoke, null);
        }
    }
}